﻿/*
ParallelPlatform Project - A web platform to distribute and share knowledge
Online Editor - An online tool to create and play animations
Copyright (C) 2011 Daniel Guerrero Martínez & Sergio Rodríguez Lumley

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package Classes{
	import flash.display.MovieClip;
	import flash.display.DisplayObject;
	
	import flash.text.TextField;
	import flash.text.TextFormat;
	
	import flash.events.MouseEvent;
	
	import fl.transitions.Tween;
	import fl.transitions.easing.*;
	import fl.transitions.TweenEvent;
	
	public class TL_ScenesDisplayer extends Panel {
		
		private static const CONTWIDTH:uint 	= 98;
		private static const CONTHEIGHT:uint 	= 70;
		private static const TIMESPEED:Number	= 0.5; // Tween duration in seconds
		
		private var container:MovieClip;
		private var parentFunc:Function;
		
		private var virtualWidth:Number; // Virtual width of all the scenes together
		private var tfStyle:TextFormat; // Style of the text
		private var cont:uint; // Current container selected
		private var inicont:uint; // Initial container selected
		private var numcont:uint; // Current number of containers
		private var ininumcont:uint; // Initial number of containers
		private var typelist:Vector.<uint>;
		private var initypelist:Vector.<uint>;
		
		private var selectedBlock:TL_SelectedBlock;
		private var addStagePanel:TL_AddStagePanel;
		private var removeStageButton:TL_RemoveStageButton;
		
		private var lastAddedContainer:Container; // Used to duplicate when adding new stages
		private var removedPanels:Vector.<DisplayObject>; // Used to store removed panels, and use them when adding new (performance and safety improvement)
		
		private var stageTweens:Array;
		private var addStageTween:Tween;
		private var removeStageTween:Tween;
		
		public function TL_ScenesDisplayer(){
			container		= new MovieClip();
			this.addChild(container);
			container.x 	= -350;
			container.y 	= 0;
			virtualWidth 	= 0;
			cont 			= 0;
			inicont			= 0;
			numcont			= 0;
			ininumcont		= 0;
			selectedBlock 	= new TL_SelectedBlock();
			selectedBlock.x	= -100;
			addStagePanel	= new TL_AddStagePanel();
			removeStageButton = new TL_RemoveStageButton();
			removedPanels	= new Vector.<DisplayObject>();
			stageTweens		= new Array();
			typelist		= new Vector.<uint>();
			initypelist		= new Vector.<uint>();
			
			addStagePanel.buttonMode = true;
			addStagePanel.useHandCursor = true;
			removeStageButton.buttonMode = true;
			removeStageButton.useHandCursor = true;
			addStagePanel.addEventListener(MouseEvent.CLICK, addContainerPressed);
			removeStageButton.addEventListener(MouseEvent.CLICK, removeContainerPressed);
			
			tfStyle 		= new TextFormat();
			tfStyle.size 	= 12;
			tfStyle.color 	= "0x333333";
			tfStyle.font 	= "_sans";
			tfStyle.align 	= "center";
		}
		
		/**
		  Moves the list to the left or to the right, note that if you want the
		  list to move to the left, a negative number should be passed as an
		  argument
		  @param num Amount of pixels to move
		*/
		public function moveList(num:Number):void{
			container.x += num;
			// We always move the list to negative positions of X
			// This is if we are at the end of the list (maximum negative)
			if(container.x < -virtualWidth+250) container.x = -virtualWidth +250;
			// This is if we are at the begining of the list (minimum negative)
			if(container.x > -150) container.x = -150;
			
			// Detect what scenes are being shown and make them visible
			// or not
			
		}
		
		public function setParentFunction(func:Function):void{
			parentFunc = func;
		}
		
		/**
		  Returns the index of the selected container
		*/
		public function getSelectedContainer():uint{
			return cont;
		}
		
		/**
		  Returns the current number of containers
		*/
		public function getNumContainers():uint{
			return numcont;
		}
		
		/**
		  Returns the current list of types of each container
		*/
		public function getContainersTypes():Vector.<uint>{
			return typelist;
		}
		
		/**
		  Returns if the current number of containers has changed
		*/
		public function hasChangedNumContainers():Boolean{
			var changed:Boolean = false;
			if(numcont != ininumcont) changed = true;
			return changed;
		}
		
		/**
		  Returns if the current number of containers has changed
		*/
		public function hasChangedCurrentContainer():Boolean{
			var changed:Boolean = false;
			if(cont != inicont) changed = true;
			return changed;
		}
		
		/**
		  Returns if the current type of containers has changed, it only
		  test the difference between the old containers type, if new
		  containers are added these are not counted (if only removed,
		  these will count too)
		*/
		public function hasChangedContainersType():Boolean{
			var changed:Boolean = false;
			var i:uint = 0;
			for(i=0; i<ininumcont && !changed; ++i){
				if(initypelist[i] != typelist[i]) changed = true;
			}
			return changed;
		}
		
		/**
		  Refills all the panels with the containers
		  @param cont Vector.<Container> with all the containers
		  @param current uint specifying the index of the current container being modified
		*/
		public function refillPanels(conts:Vector.<Container>, current:uint){
			this.clearChildren();
			
			for(var i:uint = 0; i<conts.length; ++i){
				if(i != current){
					this.addChildA(conts[i]);
				}else{
					this.addChildA(conts[i].duplicate());
				}
			}
			this.setPrepared();
			// This is due to panelRestrictions
			this.setSelectedContainer(current);
			inicont = current;
			this.centerInSelection();
		}
		
		public function clearChildren():void{
			var tmp:MovieClip;
			
			// Reset variables
			container.x = 0;
			numcont		= 0;
			ininumcont	= 0;
			if(typelist.length > 0)
				typelist	= new Vector.<uint>();
			if(initypelist.length > 0)
				initypelist	= new Vector.<uint>();
			
			// Remove all extras
			if(selectedBlock.parent != null) container.removeChild(selectedBlock);
			if(addStagePanel.parent != null) container.removeChild(addStagePanel);
			if(removeStageButton.parent != null) container.removeChild(removeStageButton);
			
			// Stop animations
			if(addStageTween != null) addStageTween.stop();
			if(removeStageTween != null) removeStageTween.stop();
			
			// Remove listeners
			while(container.numChildren > 0){
				tmp = (container.getChildAt(container.numChildren-1) as MovieClip);
				(tmp.getChildAt(tmp.numChildren-1)).removeEventListener(
						MouseEvent.DOUBLE_CLICK, onDoubleClick);
				(tmp.getChildAt(tmp.numChildren-1)).removeEventListener(
						MouseEvent.CLICK, onClick);
				(tmp.getChildAt(tmp.numChildren-2)).removeEventListener(
						MouseEvent.CLICK, cycleType);
				container.removeChild(tmp);
			}
			
			// We remove all event listeners from removed panels
			while(removedPanels.length > 0){
				tmp = (removedPanels.pop() as MovieClip);
				(tmp.getChildAt(tmp.numChildren-1)).removeEventListener(
						MouseEvent.DOUBLE_CLICK, onDoubleClick);
				(tmp.getChildAt(tmp.numChildren-1)).removeEventListener(
						MouseEvent.CLICK, onClick);
				(tmp.getChildAt(tmp.numChildren-2)).removeEventListener(
						MouseEvent.CLICK, cycleType);
			}
			
			virtualWidth = 0;
		}
		
		/*********** Private functions *************/
		
		/**
		  Sets the index of the selected container
		  @param selCont uint index of the selected container
		*/
		private function setSelectedContainer(selCont:uint):void{
			cont = selCont;
			selectedBlock.x = (this.getChildAtA(cont)).x;
		}
		
		/**
		  Sets the specified object to the right dimensions needed to display in scenes-displayer
		  (See insertChildInBackground method to understand better)
		  @param square DisplayObject to adjust
		*/
		private function setChildProperties(child:DisplayObject):void{
			child.width = CONTWIDTH;
			child.height = CONTHEIGHT;
			child.x = 0;
			child.y = 0;
		}
		
		// Adders override
		private function addChildA(child:DisplayObject, type:uint = uint.MAX_VALUE):void{
			++numcont;
			lastAddedContainer = child as Container;
			if(type == uint.MAX_VALUE)
				typelist.push(lastAddedContainer.getType());
			else
				typelist.push(type);
			(container.addChild(insertChildInBackground(child)) as TL_DisplayBlock).getTop().paintType(typelist[typelist.length-1]);
		}
		
		// Getters override
		private function getChildAtA(index:int):DisplayObject{
			return container.getChildAt(index);
		}
		
		/**
		  Prepares the displayPanel to be shown setting the
		  selectedBlock object over the rest and adding
		  remove/add buttons at the end
		*/
		private function setPrepared():void{
			this.addExtras();
			this.positionateRemoveButton();
			this.positionateAddPanel();
			ininumcont 		= numcont;
			initypelist		= typelist.concat();
			
			removedPanels	= new Vector.<DisplayObject>();
			if(stageTweens.length > 0) 
				stageTweens	= new Array();
		}
		
		/**
		  Centers the display panel in the selected one
		*/
		private function centerInSelection():void{
			this.moveList(-(CONTWIDTH+5)*cont + 50);
		}
		
		/**
		  Positionates the specified object to the last position of the scenes-displayer
		  (See insertChildInBackground method to understand better)
		  @param square DisplayObject to positionate
		*/
		private function positionateSquare(square:DisplayObject):void{
			square.x = (square.width+5)*(container.numChildren-1);
			square.y = 0;
		}
		
		/**
		  Pops the last display-scene from the container
		  @return DisplayObject the popped child
		*/
		private function popChild():DisplayObject{
			var ret:DisplayObject = container.removeChildAt(numcont-1);
			var numCh:uint = container.numChildren;
			if(selectedBlock.parent != null) --numCh;
			if(addStagePanel.parent != null) --numCh;
			if(removeStageButton.parent != null) --numCh;
			--numcont;
			virtualWidth = (CONTWIDTH+5)*numCh;
			return ret;
		}
		
		private function insertChildInBackground(child:DisplayObject):DisplayObject{
			var tmpbg:CanvasBackground 	= new CanvasBackground();
			var tmp:TL_DisplayBlock 	= new TL_DisplayBlock();
			var overtmp:TL_DisplayBlock = new TL_DisplayBlock();
			var typeCycler:TL_CycleStageType = new TL_CycleStageType();
			var tf:TextField 		= new TextField();
			tf.defaultTextFormat 	= tfStyle;
			tf.selectable 			= false;
			tf.y 					= -18;
			tf.x 					= 0;
			tf.text 				= String(container.numChildren+1);
			
			overtmp.buttonMode 			= true;
			overtmp.useHandCursor 		= true;
			overtmp.doubleClickEnabled 	= true;
			overtmp.mouseChildren		= false;
			overtmp.setID(container.numChildren);
			overtmp.addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
			overtmp.addEventListener(MouseEvent.CLICK, onClick);
			typeCycler.setID(container.numChildren);
			typeCycler.addEventListener(MouseEvent.CLICK, cycleType);
			
			tmpbg.addChild(child);
			this.setChildProperties(tmpbg);
			this.positionateSquare(tmp);
			
			tmp.addChild(tmpbg);
			tmp.addChild(tf);
			tmp.addChild(typeCycler);
			tmp.addChild(overtmp);
										// This child has not been added yet
			virtualWidth = (CONTWIDTH+5)*(container.numChildren+1);
			
			return tmp;
		}
		
		private function onClick(evt:MouseEvent):void{
			this.setSelectedContainer((evt.target as TL_DisplayBlock).getID());
		}
		
		private function onDoubleClick(evt:MouseEvent):void{
			this.setSelectedContainer((evt.target as TL_DisplayBlock).getID());
			parentFunc(evt);
		}
		
		private function cycleType(evt:MouseEvent):void{
			var chld:uint = (evt.target as TL_CycleStageType).getID();
			var nexttype:uint = typelist[chld];
			
			if(nexttype == Container.NORMAL) nexttype = Container.INSTANT;
			else if(nexttype == Container.INSTANT) nexttype = Container.READER;
			else if(nexttype == Container.READER) nexttype = Container.NORMAL;
			
			typelist[chld] = nexttype;
			(container.getChildAt(chld) as TL_DisplayBlock).getTop().paintType(nexttype);
		}
		
		private function addContainerPressed(evt:MouseEvent):void{
			this.removeExtras();
			
			if(removedPanels.length <= 0){ // Create a duplicate of last added container
				this.addChildA(lastAddedContainer.duplicate(), Container.NORMAL);
			}else{ // Pop from the removed panels and add it again
				container.addChild(removedPanels.pop());
				++numcont;
				virtualWidth = (CONTWIDTH+5)*(container.numChildren);
			}
			
			this.addExtras();
			positionateRemoveButton();
			positionateAddPanel();
		}
		
		private function removeContainerPressed(evt:MouseEvent):void{
			removedPanels.push(popChild());
			if(cont >= numcont)
				setSelectedContainer(numcont-1);
			positionateRemoveButton();
			positionateAddPanel();
		}
		
		/**
		  Positionates the remove button in the last container, if
		  there are two or more containers, else it hides it
		*/
		private function positionateRemoveButton():void{
			if(numcont > 1){
				removeStageButton.x = virtualWidth - 5 -CONTWIDTH*2;
				if(removeStageTween != null) removeStageTween.stop();
				removeStageTween = new Tween(removeStageButton, "alpha", Regular.easeOut, 0, 1, TIMESPEED, true);
			}else{
				removeStageButton.x = -1000;
			}
		}
		
		/**
		  Correctly positionates the AddPanel
		*/
		private function positionateAddPanel():void{
			//addStagePanel.x = virtualWidth + 5 -CONTWIDTH;
			if(addStageTween != null) addStageTween.stop();
			addStageTween = new Tween(addStagePanel, "x", Regular.easeOut, addStagePanel.x, virtualWidth + 5 -CONTWIDTH, TIMESPEED, true);
		}
		
		/**
		  Adds to the container all of the extras needed to correctly see the displayer
		*/
		private function addExtras():void{
			container.addChild(addStagePanel);
			container.addChild(removeStageButton);
			container.addChild(selectedBlock);
		}
		
		/**
		  Remove of the container all of the extras needed to correctly see the displayer
		*/
		private function removeExtras():void{
			container.removeChild(addStagePanel);
			container.removeChild(removeStageButton);
			container.removeChild(selectedBlock);
		}
	}
}